【Java】Thread类中的join()方法原理 您所在的位置:网站首页 java多线程 join 【Java】Thread类中的join()方法原理

【Java】Thread类中的join()方法原理

2023-09-27 02:57| 来源: 网络整理| 查看: 265

简介

join()是Thread类的一个方法。根据jdk文档的定义:

public final void join()throws InterruptedException: Waits for this thread to die.

join()方法的作用,是等待这个线程结束;但显然,这样的定义并不清晰。个人认为”Java 7 Concurrency Cookbook”的定义较为清晰:

join() method suspends the execution of the calling thread until the object called finishes its execution.

也就是说,t.join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;通常用于在main()主线程内,等待其它线程完成再结束main()主线程。我们来看看下面的例子。

例子

我们对比一下下面这两个例子,看看使用join()方法的作用是什么?

不使用join()方法的情况: public static void main(String[] args){ System.out.println("MainThread run start."); //启动一个子线程 Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println("threadA run start."); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("threadA run finished."); } }); threadA.start(); System.out.println("MainThread join before"); System.out.println("MainThread run finished."); }

运行结果如下:

MainThread run start. threadA run start. MainThread join before MainThread run finished. threadA run finished.

因为上述子线程执行时间相对较长,所以是在主线程执行完毕之后才结束。

使用了join()方法的情况: public static void main(String[] args){ System.out.println("MainThread run start."); //启动一个子线程 Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println("threadA run start."); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("threadA run finished."); } }); threadA.start(); System.out.println("MainThread join before"); try { threadA.join(); //调用join() } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("MainThread run finished."); }

运行结果如下:

MainThread run start. threadA run start. MainThread join before threadA run finished. MainThread run finished.

对子线程threadA使用了join()方法之后,我们发现主线程会等待子线程执行完成之后才往后执行。

join()的原理和作用

java层次的状态转换图

我们来深入源码了解一下join():

//Thread类中 public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); //获取当前时间 long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { //这个分支是无限期等待直到b线程结束 while (isAlive()) { wait(0); } } else { //这个分支是等待固定时间,如果b没结束,那么就不等待了。 while (isAlive()) { long delay = millis - now; if (delay threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); // Clear the native thread instance - this makes isAlive return false and allows the join() // to complete once we've done the notify_all below java_lang_Thread::set_thread(threadObj(), NULL); // 同志们看到了没,别的不用看,就看这一句 // thread就是当前线程,是啥?就是刚才例子中说的threadA线程啊。 lock.notify_all(thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); }

当子线程threadA执行完毕的时候,jvm会自动唤醒阻塞在threadA对象上的线程,在我们的例子中也就是主线程。至此,threadA线程对象被notifyall了,那么主线程也就能继续跑下去了。

可以看出,join()方法实现是通过wait()(小提示:Object 提供的方法)。 当main线程调用threadA.join时候,main线程会获得线程对象threadA的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 (也就是子线程threadA执行完毕退出的时候)

总结

首先join() 是一个synchronized方法, 里面调用了wait(),这个过程的目的是让持有这个同步锁的线程进入等待,那么谁持有了这个同步锁呢?答案是主线程,因为主线程调用了threadA.join()方法,相当于在threadA.join()代码这块写了一个同步代码块,谁去执行了这段代码呢,是主线程,所以主线程被wait()了。然后在子线程threadA执行完毕之后,JVM会调用lock.notify_all(thread);唤醒持有threadA这个对象锁的线程,也就是主线程,会继续执行。

参考资料 Java中Thread类的join方法到底是如何实现等待的?简谈Java的join()方法


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有